
/* 
 *
 * Innehåller funktioner för en länkad lista
 *
 * AUTHOR: Christian Ohlsson
 * DATE: 2001-01-30, Rev 2008-09-08 Christer Selvefors
 * Rev 2008-08-15 Per-Anders Enarsson
 * VERSION: 1.4
 ********************************************************************
 */

#include <stdio.h>                                          // För printf & scanF
#include <stdlib.h>                                         // För malloc
#include <string.h>
#include "list.h"

#define VERBOSE      1                                      //Skriv ut text på skärm
#define NO_VERBOSE   0                                      //Skriv inte ut text
#define EQUAL        0                                      //Lika
#define SMALLER     -1                                      //Mindre
#define BIGGER       1                                      //Större


extern char gName[],fName[],phoneNumber[],rFileName[];      //Globala variabler deklarerade i main
extern int nodes;                                           //Nodräknare, global

// ==================================================================
// makeNewNode
// Skapar en ny nod i listan

NodePtr makeNewNode (const int value) {
   NodePtr theNode = malloc (sizeof (ListNode));            // Allokera plats för ny nod
   if(theNode==NULL) exitPrg();
   strcpy(theNode->sgName,gName);                           // Sätt in värdet i noden
   strcpy(theNode->sfName,fName);
   strcpy(theNode->sphoneNumber,phoneNumber);
   theNode->nextPtr = NULL;                                 // Terminera nextpekaren
   nodes++;
   return theNode;                                          // Returnera den nya noden
}


// ==================================================================
// isEmpty
// Returnerar 1 om listan är tom,
// annars 0.

int isEmpty (List *myList) {
   return myList->head == NULL;                             // Om första noden pekar på
}                                                           // NULL är listan tom


// ==================================================================
// initList
// Initiera en ny lista

void initList (List *myList) {
   myList->head = myList->tail = NULL;                      // Första och sista
}                                                           // noden pekar på NULL

// ==================================================================
// destroyList
// Tar bort alla element i listan

void destroyList (List *myList) {
   while (!isEmpty (myList))                                // Så länge det finns noder
      removeFromFront (myList,NO_VERBOSE);                  // ...ta bort dom framifrån
   free (myList);                                           // Avallokera minnet till listan
   //   printf ("Listan är tom.\n");
}


// ==================================================================
// printList
// Skriver ut all data i noderna

void printList (List *myList) {
   int i=1;
   NodePtr currentPtr = malloc (sizeof (NodePtr));          //Tilldela minne...
   if(currentPtr==NULL) exitPrg();                          //öm det inte gick, avsluta prg
   NodePtr freePtr=currentPtr;                              //Spara undan pekaren för att kunna lämna tillbaka minne senare
   currentPtr = myList->head;                               // Sätt currentPtr till början
   if  (!isEmpty (myList)) {
      printf ("List contains: ");
      printf("\n***********************************************************************");
      printf ("\n*%5s *%19s *%19s *%19s *","Nbr", "Given name", "Family name", "phonenumber");
      printf("\n***********************************************************************");
      while  (currentPtr != NULL) {                         // Loopa tills vi når slutet
         
         // Skriv ut datafältet i noden
         printf ("\n|%5d |%19s |%19s |%19s |", i++,currentPtr->sgName,currentPtr->sfName,currentPtr->sphoneNumber);
         printf("\n-----------------------------------------------------------------------");
         if(i%10==1)                                        //Pausa på var tionde post
         {
            printf("\nPress enter to continue\n"); getchar();
         }
         currentPtr = currentPtr->nextPtr;                  // Gå fram ett steg i listan
      }
   }
   free (freePtr);                                          //Frigör minnet på tempnoden
}


// ==================================================================
// insertAtFront
// Sätter in ett nytt element i början på listan

void insertAtFront (List *myList, const int value) {
   NodePtr newPtr = malloc (sizeof (NodePtr));              //Tilldela minne..
   if(newPtr==NULL) exitPrg();                              //om misslycka avsluta prg
   NodePtr freePtr=newPtr;                                  //Spara pekare för att kunna lämna tillbaka minne
   newPtr = makeNewNode (value);                            // Skapa en ny nod
   
   if  (isEmpty (myList)){                                  // Om listan är tom...
      myList->head = myList->tail = newPtr;                 // head&tail pekar på nya
   }
   else {                                                   // ...annars
      newPtr->nextPtr = myList->head;                       // ny nod pekar på förra head
      myList->head = newPtr;                                // head pekar på ny nod
   }
   free(freePtr);
}
// ==================================================================
// insertAtBack
// Sätter in ett nytt element i slutet på listan

void insertAtBack (List *myList, const int value) {
   NodePtr newPtr = malloc (sizeof (NodePtr));              //Allokera minne..
   if(newPtr==NULL) exitPrg();                              //om misslyckat avsluta prg
   NodePtr freePtr=newPtr;                                  //Spara undan nod för att kunna återlämna
   newPtr = makeNewNode (value);                            // Skapa en ny nod
   
   if  (isEmpty (myList)){                                  // Om listan är tom...
      myList->head = myList->tail = newPtr;                 // head&tail pekar på nya
   }
   else {                                                   //   ...annars
      myList->tail->nextPtr = newPtr;                       // tail->next pekar på ny nod
      myList->tail = newPtr;                                // Sätt tail till nya noden
   }      
   free(freePtr);                                           //Återlämna minne
}

// ==================================================================
// removeFromFront
// Tar bort första elementet i listan element

int removeFromFront (List *myList, int ui) {
   NodePtr freePtr;                                         //Skapa en pekare som går att återlämna
   char yn='n';
   if(!isEmpty (myList)){
      freePtr=myList->head;                                 //Lagra pekare till första noden för att kunna lämna tillbaks
      if(ui==VERBOSE){                                      //Skriva på skärm och hämta y/n från anv.
         printf("Deleting: ");
         printf("\n***********************************************************************");
         printf("\n*       %19s *%19s *%19s *", "Given name", "Family name", "phonenumber");
         printf("\n***********************************************************************");
         printf("\n|       %19s |%19s |%19s | ", myList->head->sgName,myList->head->sfName,myList->head->sphoneNumber);
         printf("\n-----------------------------------------------------------------------");
         printf("\nReally delete? ('y' to delete item!)");
         scanf("%c",&yn); skipGarb();
      }
      if(yn=='y'|| ui==NO_VERBOSE)                          //Om anv. valde ja eller om vi inte skriver ut frågan
      {
         if(myList->head == myList->tail)                   // Om det bara finns en nod
            myList->head = myList->tail = NULL;             // Sätt head&tail till NULL
         else{                                              // ...annars
            myList->head = myList->head->nextPtr;           // head pekar på nästa nod
         }
         nodes--;                                           //Nu är vi ett element mindre
      }
   }
   free(freePtr);                                           //Lämna tillbaka pekare
   return 0;
}

// ==================================================================
// removeFromBack
// Tar bort sista elementet i listan element

int removeFromBack (List *myList,int ui) {
   NodePtr currentPtr,freePtr;
   char yn='n';
   
   if  (!isEmpty (myList)) {                                // Om listan inte är tom
      if(ui==VERBOSE){                                      //Skriva ut fråga till anv.?
         printf ("Deleted: ");
         printf("\n***********************************************************************");
         printf ("\n*       %19s *%19s *%19s *", "Given name", "Family name", "phonenumber");
         printf("\n***********************************************************************");
         printf ("\n|       %19s |%19s |%19s | ", myList->tail->sgName,myList->tail->sfName,myList->tail->sphoneNumber);
         printf("\n-----------------------------------------------------------------------");
         printf("\nReally delete? ('y' to delete item!)");
         scanf("%c",&yn); skipGarb();
      }
      if(yn=='y'||ui==NO_VERBOSE)                           //Om anv. valde y eller om vi inte vill visa frågan
      {
         if  (myList->head == myList->tail)                 // Om det bara finns en nod
            myList->head = myList->tail = NULL;             // Sätt head&tail till NULL
         else {                                             // ...annars
            currentPtr = myList->head;                      // Börja på första noden
            while  (currentPtr->nextPtr != myList->tail)    // ... och loopa fram till nod som pekar på sista
               currentPtr = currentPtr->nextPtr;            // Nästa nod
            myList->tail = currentPtr;                      // Sätt tail att peka på näst sista
            freePtr=currentPtr->nextPtr;                    // Lagra sista noden för att kunna lämna tillbaka minne
            currentPtr->nextPtr = NULL;                     // Terminera nextpekaren
            free(freePtr);                                  // Lämna tillbaka minnet
         }
         nodes--;
      }
   }
   return 0;                                                // Returnera nodens värde
}

// ==================================================================
// removeInList
// Raderar en nod i listan
// Funktionen letar upp plats i och visar denna post
// Användare kan ångra eller bekräfta radering

int removeInList (List *myList,int i) {
   char yn='n';
   NodePtr currentPtr = malloc (sizeof (NodePtr));          //Allokera minne
   if(currentPtr==NULL) exitPrg();                          //gick inte, avsluta
   NodePtr tmpPtr = malloc (sizeof (NodePtr));              //Allokera minne
   if(tmpPtr==NULL) exitPrg();                              //gick inte, avsluta
   NodePtr prevPtr = malloc (sizeof (NodePtr));             //Allokera minne
   if(prevPtr==NULL) exitPrg();                             //gick inte avsluta
   NodePtr freePtr1=currentPtr;                             //Spara undan pekare
   NodePtr freePtr2=tmpPtr;                                 //Spara undan pekare
   NodePtr freePtr3=prevPtr;                                //För att kunna lämna tillbaka senare
   
   currentPtr = myList->head;                               // Sätt currentPtr till början
   
   if  (!isEmpty (myList)) {                                //Om inte listan är tom
      if (i==1)                                             //Om första skall raderas..
      {
         removeFromFront(myList,VERBOSE);                   // så använd removeFromFront
         free(freePtr1);free(freePtr2);free(freePtr3);
         return 0;
      }
      
      if (currentPtr->nextPtr==NULL)                        //Om sista noden skall raderas
      {
         removeFromBack(myList,VERBOSE);                    //.. så använd removeFromBack, med ui
         free(freePtr1);free(freePtr2);free(freePtr3);      //Återlämna pekare
         return 0;
      }
      
      while  (currentPtr != NULL)                           //Så länge vi har noder kvar
      {                                                
         
         switch(i)
         {
               
            case 0: free(freePtr1);free(freePtr2);free(freePtr3); return 0; break;                  //Hittade inget att radera så avbryt
               
            case 1:                                         //Vi har hittat noden som skall raderas      
               printf ("Deleting: ");
               printf("\n***********************************************************************");
               printf ("\n*       %19s *%19s *%19s *", "Given name", "Family name", "phonenumber");
               printf("\n***********************************************************************");
               printf ("\n|       %19s |%19s |%19s | ", currentPtr->sgName,currentPtr->sfName,currentPtr->sphoneNumber);
               printf("\n-----------------------------------------------------------------------");
               printf("\nReally delete? ('y' to delete item!)");
               scanf("%c",&yn); skipGarb();
               if(yn=='y')
               {
                  tmpPtr=currentPtr->nextPtr;               //Lagra nuvarande nextpekare
                  currentPtr=prevPtr;                       //Backa en post
                  currentPtr->nextPtr=tmpPtr;               //och så peka om noden innan den raderade till den efter
                  nodes--;                                  //minska nod-räknaren
               }
               free(freePtr1);free(freePtr2);free(freePtr3);
               return 0;                                    //Klart
               
               break;                                       //Hoppa ur
         }
         
         prevPtr = currentPtr;                              //Lagra nuvarande nod
         currentPtr = currentPtr->nextPtr;                  // Gå fram ett steg i listan
         i--;                                               //Minska räknaren
      }
      
      printf ("\n");
      
   }
   free(freePtr1);free(freePtr2);free(freePtr3);            //Återlämna minne
   return 0;
}

// ==================================================================
// insertSortedToList
// Lägger in en nod sorterad i listan, efternamn
// Om inkommande data är exakt som en befintlig nod så skippas denna.

int insertSortedToList (List *myList,int value) {
   int compareResult;
   
   if  (nodes==0) {                                         //Om första noden..
      insertAtFront(myList,value);                          //sätt in först(eller sist)
      return 0;                                             //Klart
   }
   
   NodePtr currentPtr = malloc (sizeof (NodePtr));          //Akllokera minne
   if(currentPtr==NULL) exitPrg();                          //gick inte, avbryt
   NodePtr freePtr1=currentPtr;                             //Spara pekare för att kunna återlämna senare
   currentPtr = myList->head;                               // Sätt currentPtr till början
   
   if  (nodes==1) {                                         //Om två noder och..
      compareResult=checkSmallerOrEqual(currentPtr->sfName, currentPtr->sgName, currentPtr->sphoneNumber);
      if(compareResult==EQUAL){                             //Om noder exakt samma
         free(freePtr1);                                    //Frigör minnet
         return 0;                                          //och återgå utan att göra ngt
      }
      
      if(compareResult==SMALLER){                           //Om posten mindre
         insertAtFront(myList,value);                       // så använd insertAtFront
         free(freePtr1);                                    //Lämna tillbaka minne
         return 0;
      }
      
      else{
         insertAtBack(myList, value);                       //Om inte lägre så lägg in sist.
         free(freePtr1);                                    //Lämna tillbaka minne
         return 0;
      }   
      
   }   
   
   //Om två eller fler noder
   compareResult=checkSmallerOrEqual(currentPtr->sfName, currentPtr->sgName, currentPtr->sphoneNumber);
   if(compareResult==SMALLER){                              //Om mindre
      if(currentPtr==myList->head){                         //och nuvarande är första
         if(compareResult==EQUAL){                          //Om exakt samma..
            free(freePtr1);                                 //lämna tillbaka minne och..
            return 0;                                       //Återgå utan att spara nya noden
         }
      }
      insertAtFront(myList, value);{                        //så sätt in ny nod först
         free(freePtr1);                                    //Återlämna minne
         return 0;
      }
   }
   
   NodePtr prevPtr = malloc (sizeof (NodePtr));             //Reservera minne
   
   if(prevPtr==NULL) exitPrg();                             //om det inte gick, avsluta
   NodePtr freePtr2=prevPtr;                                //Spara undan för att unna återlämna
   compareResult=checkSmallerOrEqual(currentPtr->sfName, currentPtr->sgName, currentPtr->sphoneNumber);
   while  (compareResult>=EQUAL) {                          //Om ny nod är högre än nuvarande
      
      if(currentPtr==myList->tail){                         //och sist
         if(compareResult==EQUAL){                          //Om exakt samma
            free(freePtr1);free(freePtr2);                  //återlämna minne
            return 0;                                       //och återgå utan att spara ngt
         }
         
         insertAtBack(myList, value);{                      //så sätt in sist
            free(freePtr1);                                 //Återlämna lånat minne
            free(freePtr2);
            return 0;
         }
      }
      
      if(compareResult ==EQUAL){                            //Om exakt samma
         free(freePtr1); free(freePtr2);                    //Lämna tillbaka minne
         return 0;
      }
      
      prevPtr = currentPtr;                                 //Annars lagra nuvarande nod
      currentPtr=currentPtr->nextPtr;                       //och gå till nästa
      compareResult=checkSmallerOrEqual(currentPtr->sfName, currentPtr->sgName, currentPtr->sphoneNumber);
      
   }
   //Vi har hittat platsen där nya skall in innan
   NodePtr newPtr = malloc (sizeof (NodePtr));              //Allokera minne
   if(newPtr==NULL) exitPrg();                              //Avsluta om inget minne gavs
   NodePtr freePtr3=newPtr;                                 //Spara undan för att återlämna senare
   newPtr = makeNewNode (value);                            //Skapa en ny nod
   newPtr->nextPtr=currentPtr;                              //kopiera nuvarande nods next-pekare till den nya
   prevPtr->nextPtr=newPtr;                                 //sätt adressen till nya noden som föregående nods next-pekare
   free(freePtr1);free(freePtr2);free(freePtr3);
   return 0;
}


// ==================================================================
// saveList
// Sparar all data
// Kontrollerar om fil finns och i så fall får användaren valet 
// att avbryta, appenda, skriva över eller spara som ny fil.

int saveListToFile (List *myList) {
   FILE *fp;                                                //Skapa filpekare
   char *fileName(),*lFileName;
   int sChoise=0,openSuccess=0,again=1;
   
   while(again==1){                                         //Loopa tills brytvilkor
      if((fp=fopen(lFileName=fileName(), "r"))!=NULL){      //Läs in namn och test on filen går att öppna
         sChoise=checkIfOverwriteFile();                    //Fråga user vad göra
         if (sChoise==0)                                    //Avbryt
            return 0;
         if (sChoise==1){                                   //Skriv över
            fclose(fp);                                     //Stäng filen
            openSuccess=((fp=fopen(lFileName,"w"))!=NULL);  //Och öppna i skrivläge
            again=0;
         }
         
         if (sChoise==2){                                   //Appenda
            fclose(fp);                                     //Stäng fil
            openSuccess=((fp=fopen(lFileName, "a"))!=NULL); //Skriv hopslagna lista till fil
            again=0;
         }   
      }
      else{                                                 //Det fanns ingen fil med namnet så öppna ny fil i skrivläge
         openSuccess=((fp=fopen(lFileName,"w"))!=NULL);
         again=0;
      }
      
   }
   {
      NodePtr currentPtr = malloc (sizeof (NodePtr));       //Allokera minne
      if(currentPtr==NULL) exitPrg();                       //Avsluta om mislyckat
      NodePtr freePtr=currentPtr;                           //Spara pekare för att kunna återläman senare
      currentPtr = myList->head;                            // Sätt currentPtr till början
      if(!isEmpty (myList)) {
         while  (currentPtr != NULL) {                      // Loopa tills vi når slutet
            fprintf (fp,"%50s%50s%50s",currentPtr->sgName,
                     currentPtr->sfName,
                     currentPtr->sphoneNumber);
            currentPtr = currentPtr->nextPtr;               // Gå fram ett steg i listan
         }
      }
      free(freePtr);                                        //Återläman minne
   }
   strcpy(rFileName,lFileName);                             //Spara filnamnet 
   fclose(fp);
   return 0;
}

// ==================================================================
// openList
// Laddar data
int openListFromFile (List *myList,char *lFileName) {
   FILE *fp;
   
   if((fp=fopen(lFileName,"r"))!=NULL){                     //öppna fil i läsläge
      while(1==1){
         if(fscanf(fp,"%50s",gName)==EOF)                   //Läs 50 tecken
            break;                                          //Bryt om EOF
         if(fscanf(fp,"%50s",fName)==EOF)
            break;
         if(fscanf(fp,"%50s",phoneNumber)==EOF)
            break;
         insertSortedToList(myList,0);                      //Stoppa in i listan
      }
   }
   else{
      printf("Could not open %s \n",lFileName);             //Gick inte att öppna listan
      getchar();
   }
   strcpy(rFileName,lFileName);                             //spara undan filnamnet
   fclose(fp);
   return 0;
}

// ==================================================================
// searchInList
// Söker och skriver ut data i noderna

int searchInList (List *myList,int sChoise) {
   int i=1,fN=1,gN=1,phN=1;
   NodePtr currentPtr = malloc (sizeof (NodePtr));
   if(currentPtr==NULL) exitPrg();
   NodePtr freePtr=currentPtr;                              //Spara undan pekare till currentptr för att kunna lämna tillbaka
   currentPtr = myList->head;                               // Sätt currentPtr till början
   if  (!isEmpty (myList)) {
      printf ("Search result: ");
      printf("\n***********************************************************************");
      printf ("\n*%5s *%19s *%19s *%19s *","Nbr", "Given name", "Family name", "phonenumber");
      printf("\n***********************************************************************");
      while  (currentPtr != NULL) {                         // Loopa tills vi når slutet
         
         // Skriv ut datafältet i noden om exakt träff
         if(sChoise==1){
            if(strcmp(gName,"0")!=0)
               fN=strcmp(gName, currentPtr->sgName);
            else fN=99;
            if(strcmp(fName,"0")!=0)
               gN=strcmp(fName, currentPtr->sfName);
            else gN=99;
            if(strcmp(phoneNumber,"0")!=0)
               phN=strcmp(phoneNumber , currentPtr->sphoneNumber);
            else phN=99;
         }
         // Skriv ut datafältet i noden om delvis träff
         if(sChoise==2){
            if(strcmp(gName,"0")!=0)
               if(strstr(currentPtr->sgName,gName))
                  fN=0;
               else fN=99;
            if(strcmp(fName,"0")!=0)
               if(strstr(currentPtr->sfName,fName))
                  gN=0;
               else gN=99;
            if(strcmp(phoneNumber,"0")!=0)
               if(strstr(currentPtr->sphoneNumber, phoneNumber))
                  phN=0;
               else phN=99;
         }
         if(fN==0||gN==0||phN==0){
            printf ("\n|%5d |%19s |%19s |%19s | ", i,currentPtr->sgName,currentPtr->sfName,currentPtr->sphoneNumber);
            printf("\n-----------------------------------------------------------------------");
            if(i%10==0){
               printf("\nPress enter to continue"); getchar();
            }
         }
         currentPtr = currentPtr->nextPtr;                  // Gå fram ett steg i listan
         i++;
      }
      
   }
   printf("\npress enter to continue");
   getchar();
   free (freePtr);
   printf ("\n");
   return 0;
}

// ==================================================================
// Check if smaller or equal
// 

int checkSmallerOrEqual(char *lfName, char *lgName,char *lphoneNumber){
   if(strcmp(lfName,fName)>0)   return SMALLER;             //Om nya efternamn mindrre
   else if(strcmp(lfName, fName)<0) return BIGGER;          //eller större
   else if(strcmp(lgName,gName)>0) return SMALLER;          //annars kolla förnamn på samma sätt
   else if(strcmp(lgName, gName)<0) return BIGGER;
   else if(strcmp(lphoneNumber,phoneNumber)<0) return SMALLER;//om även dessa samm kolla telefonnummer
   else if(strcmp(lphoneNumber,phoneNumber)>0)return BIGGER;
   return EQUAL;                                            //Exakt samma
}
